﻿//  Copyright (c) 2011 Ray Liang (http://www.dotnetage.com)
//  Licensed MIT: http://www.opensource.org/licenses/mit-license.php

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Web;
using DNA.Utility;

namespace DNA.Mvc.Data.Xml
{
    public class PackageXmlRepository : IPackageRepository
    {
        /// <summary>
        /// The physical storage path. 
        /// </summary>
        protected string storagePath = "";

        /// <summary>
        /// Present the visual storage path.
        /// </summary>
        protected string vPath = "";
        protected HttpContextBase httpContext = null;

        protected HashSet<Package> Dbset = new HashSet<Package>();

        public PackageXmlRepository(string vpath) : this(new HttpContextWrapper(HttpContext.Current), vpath) { }

        public PackageXmlRepository(HttpContextBase context, string vpath)
        {
            if (!vpath.StartsWith("~/"))
                throw new Exception("The storage path must be relavite path");
            vPath = vpath;
            httpContext = context;
            storagePath = context.Server.MapPath(vpath);
            PreLoadPackages();
        }

        protected string ResolveFileName(string virtualPath)
        {
            return httpContext.Server.MapPath(virtualPath);
        }

        protected virtual void PreLoadPackages()
        {
            var subPaths = Directory.GetDirectories(storagePath);
            foreach (var dir in subPaths)
            {
                var fs = Directory.GetFiles(dir, "manifest.xml");
                if (fs.Length > 0)
                {
                    try
                    {
                        var descriptor = XmlSerializerUtility.DeserializeFormXmlFile<PackageDescriptor>(fs[0]);
                        var pkg =PackageConverter.Convert(descriptor);
                        var fileInfo = new FileInfo(fs[0]);
                        pkg.Url = vPath + (vPath.EndsWith("/") ? "" : "/") + fileInfo.Directory.Name + "/" + fileInfo.Name;
                        Dbset.Add(pkg);
                    }
                    catch
                    {
                        continue;
                    }
                }
            }
        }

        public IQueryable<Package> All()
        {
            return Dbset.AsQueryable();
        }

        public IQueryable<Package> All(out int total, int index = 0, int size = 50)
        {
            int skipCount = index * size;
            var _resultSet = skipCount == 0 ? Dbset.Take(size) : Dbset.Skip(skipCount).Take(size);
            total = _resultSet.Count();
            return _resultSet.AsQueryable();
        }

        public IQueryable<Package> Filter(System.Linq.Expressions.Expression<Func<Package, bool>> predicate)
        {
            return Dbset.Where(predicate.Compile()).AsQueryable();
        }

        public IQueryable<Package> Filter<Key>(System.Linq.Expressions.Expression<Func<Package, Key>> sortingSelector, System.Linq.Expressions.Expression<Func<Package, bool>> filter, out int total, SortingOrders sortby = SortingOrders.Asc, int index = 0, int size = 50)
        {
            int skipCount = index * size;
            var _resultSet = filter != null ? Dbset.Where(filter.Compile()).AsQueryable() : Dbset.AsQueryable();
            _resultSet = sortby == SortingOrders.Asc ? _resultSet.OrderBy(sortingSelector).AsQueryable() : _resultSet.OrderByDescending(sortingSelector).AsQueryable();
            _resultSet = skipCount == 0 ? _resultSet.Take(size) : _resultSet.Skip(skipCount).Take(size);
            total = _resultSet.Count();
            return _resultSet.AsQueryable();
        }

        public bool Contains(System.Linq.Expressions.Expression<Func<Package, bool>> predicate)
        {
            return Dbset.Where(predicate.Compile()).Count() > 0;
        }

        public Package Find(params object[] keys)
        {
            return Dbset.FirstOrDefault(p => p.Url.Equals(keys[0].ToString()));
        }

        public Package Find(System.Linq.Expressions.Expression<Func<Package, bool>> predicate)
        {
            return Dbset.FirstOrDefault(predicate.Compile());
        }

        public Package Create(Package t)
        {
            //if (Find(t.Url) != null) throw new Exception("Object already exists");

            var pkgPath = this.storagePath + (this.storagePath.EndsWith("\\") ? "" : "\\") + t.Name;
            var pkgFileName = pkgPath + "\\manifest.xml";

            if (!Directory.Exists(pkgPath))
                Directory.CreateDirectory(pkgPath);

            if (t.Images.Count > 0)
            {
                var imgPath = pkgPath + "\\images";
                if (!Directory.Exists(imgPath))
                    Directory.CreateDirectory(imgPath);

                foreach (var img in t.Images)
                {
                    if (!string.IsNullOrEmpty(img.Url) && (File.Exists(img.Url)))
                    {
                        var imgFileName = Path.GetFileName(img.Url);
                        var srcImageFilename = img.Url;
                        var destImageFilename = imgPath + "\\" + imgFileName;
                        File.Copy(srcImageFilename, destImageFilename);
                        img.Url = "images/" + imgFileName;

                        try
                        {
                            using (var imgInstance = Image.FromFile(srcImageFilename))
                            {
                                img.Width = imgInstance.Width;
                                img.Height = imgInstance.Height;
                            }
                        }
                        catch
                        {
                            continue;
                        }
                    }
                }
            }

            Dbset.Add(t);
            t.Url = pkgFileName;
            XmlSerializerUtility.SerializeToXmlFile(pkgFileName, PackageConverter.Convert(t));
            return t;
        }

        public void Delete(Package t)
        {
            var path = System.IO.Path.GetDirectoryName(HttpContext.Current.Server.MapPath(t.Url)) + "\\";
            if (System.IO.Directory.Exists(path))
                System.IO.Directory.Delete(path, true);
            Dbset.Remove(t);
        }

        public int Delete(System.Linq.Expressions.Expression<Func<Package, bool>> predicate)
        {
            var pkgs = Filter(predicate);
            foreach (var pkg in pkgs)
                Delete(pkg);
            return pkgs.Count();
        }

        public Package Update(Package t)
        {
            var target = Find(t.Url);
            if (target == null) throw new Exception("Object not found");
            Delete(target);
            Create(t);
            return t;
        }

        public void Dispose()
        {
            Dbset.Clear();
            GC.SuppressFinalize(Dbset);
            GC.SuppressFinalize(this);
        }

        public virtual int Count()
        {
            return Dbset.Count;
        }

        public virtual int Count(System.Linq.Expressions.Expression<Func<Package, bool>> predicate)
        {
            return Dbset.Count(predicate.Compile());
        }

        //public virtual void WriteTo(Stream stream)
        //{ 
        //   //using (var zip=new DNA.IO.Compress.ZipCompress(
        //}


        public int Submit()
        {
            return 0;
        }
    }
}
